/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.entity.methods;

import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspAssociationKey;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.generator.DotNetToJavaStringHelper;
import com.inspur.edp.cef.generator.overall.CefGeneratorContext;
import com.inspur.edp.cef.generator.overall.Utils;
import com.inspur.edp.cef.generator.repository.base.assemblerGenerator.DataTypeAssemblerGenCtx;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonModel;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

public class InitAssociationInfoMethodGenerator extends ClassMethodGenerator {

  protected DataTypeAssemblerGenCtx assGenCtx;
  private boolean isExtend;
  private IGspCommonModel refModel;

  public InitAssociationInfoMethodGenerator(DataTypeAssemblerGenCtx assGenCtx, boolean isExtend) {
    this.assGenCtx = assGenCtx;
    this.isExtend = isExtend;
  }

  private IGspCommonDataType getCommonObject() {
    return assGenCtx.getDataType();
  }

  @Override
  protected Block buildMethodBody() {
    Block block = ast.newBlock();
    ArrayList<Statement> statements = new ArrayList<Statement>();
//		Iterable<IGspCommonField> associationElements = new ArrayList<>();
    for (IGspCommonField field : getCommonObject().getContainElements()) {
        if (field.getHasAssociation()) {
            buildInitStatement(field, statements);
        }
    }
//		associationElements = getCommonObject().getContainElements().Where(item => item.getHasAssociation());
//		for (var associationElement : associationElements)
//		{
//			BuildInitStatement(associationElement, statements);
//		}
    block.statements().addAll(statements);
    return block;
//		return SyntaxFactory.Block(statements.toArray(new StatementSyntax[]{}));
  }


  private void buildInitStatement(IGspCommonField element, ArrayList<Statement> statements) {
    if (element.getChildAssociations() == null || element.getChildAssociations().size() < 1) {
      return;
    }
    for (GspAssociation association : element.getChildAssociations()) {
      buildInitStatement(association, statements);
    }

  }
//    private IGspCommonModel getRefModel(GspAssociation association) {
//        return (IGspCommonModel) ((commonService.getRefMetadata(association.getRefModelID()).getContent() instanceof IGspCommonModel) ? ServiceManager.<IRefCommonService>GetService().GetRefMetadata(association.getRefModelID()).getContent() : null);
//    }

  private void buildInitStatement(GspAssociation association, ArrayList<Statement> statements) {
    GspMetadata metadata = assGenCtx.getRepoGenCtx().getCefGenCtx().getMetadataJitContext().
        getMetadata(association.getRefModelID());
    // GspMetadata metadata = CefGenServiceManager.getLcmDtService().getRefMetadata(association.getRefModelID());
    refModel = (IGspCommonModel) ((metadata.getContent() instanceof IGspCommonModel) ? metadata
        .getContent() : null);
    DataValidator.checkForNullReference(refModel, "RefModel");

    IGspCommonField belongElement = association.getBelongElement();
    String sourceId = getSourceElementId(association);
    if (DotNetToJavaStringHelper.isNullOrEmpty(sourceId)) {
      throw new RuntimeException(
          "#GSPBefError#" + "关联字段【" + belongElement.getName() + "】缺少关联关系" + "#GSPBefError#");
    }

    // region 获取创建列集合实例的语法树
    String varName = "refColumns" + "_" + refModel.getCode() + "_" + belongElement.getCode();
    Statement initDicStatement = getInitDicStatement(varName);
    statements.add(initDicStatement);
    // endregion

    // region 获取添加列语法树
    buildRefColumnsStatement(varName, association, statements);
    // endregion

    // region 获取生成repository的语法树构造
    String repositoryVarName = "repository_" + refModel.getCode() + "_" + belongElement.getCode();
    //var repositoryStatement = GetRepositoryStatement(repositoryVarName, refModel.getCode());
    //statements.add(repositoryStatement);

    // endregion

    // region 获取AddAssociation的语法树
    IGspCommonObject targetObject = null;
    if (DotNetToJavaStringHelper.isNullOrEmpty(association.getRefObjectID())) {
      targetObject = refModel.getMainObject();
    } else {
      targetObject = getObjectByID(refModel, association.getRefObjectID());
    }
    if (targetObject == null) {
      throw new RuntimeException("找不到实体节点");
    }

    IGspCommonElement sourceElement = targetObject.findElement(sourceId);
    //wj-repository已改
    CefGeneratorContext cefContext = assGenCtx.getRepoGenCtx().getCefGenCtx();

    String classFullName = belongElement.getBelongObject().getGeneratedEntityClassInfo()
        .getClassFullName();

    TypeInfo assDataType;
      if (belongElement.getIsUdt()) {
          assDataType = Utils
              .getUdtType(belongElement.getUdtPkgName(), belongElement.getUdtID(), cefContext);
      } else {
          assDataType = new TypeInfo(Utils.getAssociationTypeName(belongElement));
          assDataType.setTypePackageName(
              belongElement.getBelongObject().getGeneratedEntityClassInfo().getClassNamespace());
//        assDataType.setTypePackageName(belongElement.getBelongObject());
      }
//        var assembly = cefContext.getProjectApiAssembly();
//        String assDataTypeName = Utils.getNativeType()
//                Utils.getPropertyType(assembly.GetType(classFullName), belongElement.getLabelID());

    Statement addStatement = getAddAssociationStatement(
        refModel.getGeneratedConfigID(),
        targetObject.getCode(),
        belongElement.getLabelID(),
        sourceElement.getLabelID(),
        repositoryVarName,
        varName,
        assDataType);
    statements.add(addStatement);

    // endregion

  }

  private IGspCommonObject getObjectByID(IGspCommonModel model, String objectID) {

    for (IGspCommonObject item : model.getAllObjectList()) {
      if (objectID.equals(item.getID())) {
        return item;
      }
    }
    return null;
  }

//    private LocalDeclarationStatementSyntax getRepositoryStatement(String varName, String configId) {
//        //SyntaxFactory.IdentifierName("ServiceManager"),
//        //SyntaxFactory.IdentifierName("IRepositoryFactory")
//        return SyntaxFactory.LocalDeclarationStatement(SyntaxFactory.VariableDeclaration(SyntaxFactory.IdentifierName("var")).WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier(varName)).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, TypeUtils.getTypeSyntax(ServiceManager.class), SyntaxFactory.GenericName(SyntaxFactory.Identifier("GetService")).WithTypeArgumentList(SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(TypeUtils.getTypeSyntax(IRepositoryFactory.class)))))), SyntaxFactory.IdentifierName("CreateRepository"))).WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(configId)))))))))));
//    }
//
//    private ArgumentSyntax getVariableArgument(String varName) {
//        return SyntaxFactory.Argument(SyntaxFactory.IdentifierName(varName));
//    }

  private Statement getAddAssociationStatement(String configId, String nodeCode,
      String sourceColumn,
      String targetColumn, String repositoryName, String columnsName,
      TypeInfo associationType) {
    SuperMethodInvocation invocation = ast.newSuperMethodInvocation();
    invocation.setName(ast.newSimpleName("addAssociationInfo"));

    StringLiteral paramConfigId = ast.newStringLiteral();
    paramConfigId.setLiteralValue(configId);

    StringLiteral paramNodeCode = ast.newStringLiteral();
    paramNodeCode.setLiteralValue(nodeCode);

    StringLiteral paramSource = ast.newStringLiteral();
    paramSource.setLiteralValue(sourceColumn);

    StringLiteral paramTarget = ast.newStringLiteral();
    paramTarget.setLiteralValue(targetColumn);

    TypeLiteral paramAssoType = ast.newTypeLiteral();
    paramAssoType.setType(ast.newSimpleType(ast.newName(associationType.getTypeFullName())));

    SimpleName paramMap = ast.newSimpleName(columnsName);

    invocation.arguments().add(paramConfigId);
    invocation.arguments().add(paramNodeCode);
    invocation.arguments().add(paramSource);
    invocation.arguments().add(paramTarget);
    invocation.arguments().add(paramAssoType);
    invocation.arguments().add(paramMap);

    return ast.newExpressionStatement(invocation);
  }

  private Statement getAddDicStatement(String varName, String sourceElement, String targetElement) {

    MethodInvocation invocation = ast.newMethodInvocation();
    invocation.setExpression(ast.newSimpleName(varName));
    invocation.setName(ast.newSimpleName("put"));
    StringLiteral arg1 = ast.newStringLiteral();
    arg1.setLiteralValue(sourceElement);
    StringLiteral arg2 = ast.newStringLiteral();
    arg2.setLiteralValue(targetElement);
    invocation.arguments().add(arg1);
    invocation.arguments().add(arg2);

    return ast.newExpressionStatement(invocation);
//        return SyntaxFactory.ExpressionStatement(
//                SyntaxFactory.InvocationExpression(
//                        SyntaxFactory.MemberAccessExpression(
//                                SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(varName),
//                                SyntaxFactory.IdentifierName("Add")))
//                        .WithArgumentList(SyntaxFactory.ArgumentList(
//                                SyntaxFactory.<ArgumentSyntax>SeparatedList(
//                                        new SyntaxNodeOrToken[]{
//                                                SyntaxFactory.Argument(
//                                                        SyntaxFactory.LiteralExpression(
//                                                                SyntaxKind.StringLiteralExpression,
//                                                                SyntaxFactory.Literal(sourceElement))),
//                                                SyntaxFactory.Token(SyntaxKind.CommaToken),
//                                                SyntaxFactory.Argument(
//                                                        SyntaxFactory.LiteralExpression(
//                                                                SyntaxKind.StringLiteralExpression,
//                                                                SyntaxFactory.Literal(targetElement)))}))));
  }

  private Statement getInitDicStatement(String varName) {

    ParameterizedType type = ast
        .newParameterizedType(ast.newSimpleType(ast.newSimpleName("HashMap")));
    type.typeArguments().add(ast.newSimpleType(ast.newSimpleName("String")));
    type.typeArguments().add(ast.newSimpleType(ast.newSimpleName("String")));

    VariableDeclarationFragment frag = ast.newVariableDeclarationFragment();
    frag.setName(ast.newSimpleName(varName));
    ClassInstanceCreation typeCreation = ast.newClassInstanceCreation();
    typeCreation.setType(type);
    frag.setInitializer(typeCreation);

    VariableDeclarationStatement statement = ast.newVariableDeclarationStatement(frag);
    ParameterizedType type1 = ast
        .newParameterizedType(ast.newSimpleType(ast.newSimpleName("HashMap")));
    addImport(HashMap.class.getTypeName());
    type1.typeArguments().add(ast.newSimpleType(ast.newSimpleName("String")));
    type1.typeArguments().add(ast.newSimpleType(ast.newSimpleName("String")));
    statement.setType(type1);
    return statement;
//		return SyntaxFactory.LocalDeclarationStatement(
//				SyntaxFactory.VariableDeclaration(
//						SyntaxFactory.IdentifierName("var")).
//						WithVariables(SyntaxFactory.SingletonSeparatedList(
//								SyntaxFactory.VariableDeclarator(
//										SyntaxFactory.Identifier(varName))
//										.WithInitializer(SyntaxFactory.EqualsValueClause(
//												SyntaxFactory.ObjectCreationExpression(
//														TypeUtils.getTypeSyntax(
//																java.util.HashMap<String, String>.class)).
//														WithArgumentList(SyntaxFactory.ArgumentList()))))));
  }

  private String getSourceElementId(GspAssociation association) {
    IGspCommonField belongElement = association.getBelongElement();
    for (GspAssociationKey associationKey : association.getKeyCollection()) {
      if (associationKey.getTargetElement().equals(belongElement.getID())) {
        return associationKey.getSourceElement();
      }
    }
    return null;
  }

  @Override
  protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
    return new ArrayList<Modifier.ModifierKeyword>(
        Arrays.asList(new Modifier.ModifierKeyword[]{Modifier.ModifierKeyword.PUBLIC_KEYWORD}
        ));
  }

  @Override
  protected boolean getIsOverride() {
    return true;
  }

  @Override
  protected String getMethodName() {
    return "initAssociationInfo";
  }

  @Override
  protected ArrayList<ParameterInfo> getParameterCollection() {
    return new ArrayList<ParameterInfo>();
  }

  @Override
  protected TypeInfo getReturnType() {
    return new TypeInfo("void");
  }

  private void buildRefColumnsStatement(String varName, GspAssociation association,
      ArrayList<Statement> statements) {

    for (IGspCommonField refElement : association.getRefElementCollection()) {
      IGspCommonElement targetElement = refModel.findElementById(refElement.getRefElementId());
      if (targetElement == null) {
        throw new RuntimeException("没有获取到关联带出字段的引用字段，当前实体：" + getCommonObject().getCode() + "，关联实体："
            + refModel.getCode() + ",字段标签：" + refElement.getLabelID());
      }
      Statement statement =
          getAddDicStatement(varName, refElement.getLabelID(), targetElement.getLabelID());
      statements.add(statement);
    }
  }
}
